#pragma once 

#include <memory>

namespace tb {

namespace dispatch {

struct ITask {
    typedef std::shared_ptr<ITask> Ptr;

    virtual ~ITask() {}

    virtual void Execute() = 0;

    virtual void Cancle() = 0;

    virtual bool IsCancled() = 0;

    virtual bool IsExecuted() = 0;
};

template <typename FunctorType>
struct FunctionTask : public ITask {
    FunctionTask(FunctorType fn) : _fn(fn), _isCancled(false), _isExecuted(false) {}

    ~FunctionTask() {}

    void Execute() override {
        try { if (!_isCancled) _fn(); _isExecuted = True; } catch(...) {}
    }

    void Cancle() override {
        _isCancled = true;
    }

    bool IsCancled() override {
        return _isCancled;
    }

    bool IsExecuted() override {
        return _isExecuted;
    }

    void WaitingForHandled() {
        int k = 0;
        while (!IsExecuted()) yield(k++);
    }

private:
    FunctorType _fn;
    bool _isCancled;
    bool _isExecuted;
};

struct IWoker {
    typedef std::shared_ptr<IWorker> Ptr;

    template <typename FunctorType>
    ITask::Ptr Schedule(FunctorType fn, int delay = 0) {
        this.Schedule(ITask::Ptr(new FunctionTask<FunctorType>(fn)), delay);
    }

    template <typename FunctorType>
    ITask::Ptr SchedulePeriodically(
        FunctorType fn, 
        int initialDelayMillisecs = 0, 
        int periodMillisecs = 1) {
        this.SchedulePeriodically_(ITask::Ptr(new FunctionTask<FunctorType>(fn)), delay);
    }

    virtual bool IsWorkerThread() = 0;

    virtual ITask *Schedule_(const ITask::Ptr &task, int delay = 0);

    virtual ITask *SchedulePeriodically_(
        const ITask::Ptr &task, 
        int initialDelayMillisecs = 0, 
        int periodMillisecs = 1);
};

template <typename ValueType>
static void _GenerateAndPassToComsumer(
    const std::function<ValueType (void)> &generator, 
    const IWoker::Ptr &consumerWorker, 
    const std::function<void (const ValueType &)> &consumer) {
    ValueType val = generator();
    if (consumerWorker->IsWorkerThread()) {
        consumer(val);
    } else {
        consumer->Schedule([val, consumer]() {
            consumer(val);
        });
    }
}

/**
 * a helper for tramsiting data from a worker to another
 *  
 * @param generatorWorker the worker of generator
 * @param generator the generator function
 * @param consumerWorker ther worker of comsumer
 * @param consumer the comsumer function
 * @return void
*/
template <typename ValueType>
static void Transmit(
    const IWoker::Ptr &generatorWorker, 
    const std::function<ValueType (void)> &generator,
    const IWoker::Ptr &consumerWorker,
    const std::function<void (const ValueType &)> &consumer) {
    if (generatorWorker->IsWorkerThread()) {
        _GenerateAndPassToComsumer<ValueType>(generator, consumerWorker, consumer);
    } else {
        generatorWorker->Schedule([generator, consumerWorker, consumer]() {
            _GenerateAndPassToComsumer<ValueType>(generator, consumerWorker, consumer);
        });
    }
}

} // end namespace dispatch

} // end namespace tb